﻿#region Opis pliku
//-----------------------------------------------------------------------------
// StateManager.cs
// Autor: Jan Czarnowski
// 22.10.2012
//-----------------------------------------------------------------------------
#endregion

#region Using...

using System;
using System.Collections.Generic;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Content;

#endregion

namespace pogra
{
    /// <summary>
    /// Klasa zajmująca się zarządzaniem ekranami gry. Dziedziczy z DrawableGameComponent
    /// i musi byc dodana do listy komponentów danego obiektu Game. Przechowuje w sobie
    /// listę obiektów typu GameScreen, których rysowaniem i danymi wejściowymi zarządza.
    /// </summary>
    public class ScreenManager : DrawableGameComponent
    {
        #region pola i właściwości

        // klasa przekazywana do odpowiednich ekranów
        InputState input = new InputState();

        // lista ekranów gry zarządzanych przez ten obiekt
        List<GameScreen> screenList = new List<GameScreen>();

        // zapasowa lista używana w metodzie Update
        List<GameScreen> screensToUpdate = new List<GameScreen>();

        bool isInitialised = false;

        // do uzycia przez wszystkie ekrany zeby nie tworzyly lokalnych kopii
        ExtendedSpriteBatch exSpriteBatch;

        // czcionka do użycia przez wszystkie ekrany
        SpriteFont font;

        // wymiary viewport do użycia przez ekrany
        Rectangle viewRect;

        /// <summary>
        /// Obiekt ExtendedSpriteBatch, niezbędny do rysowania wszystkim ekranom
        /// </summary>
        public ExtendedSpriteBatch ExtendedSpriteBatch
        {
            get { return exSpriteBatch; }
        }

        /// <summary>
        /// Standardowa czcionka do użycia przez ekrany
        /// </summary>
        public SpriteFont Font
        {
            get { return font; }
        }

        /// <summary>
        /// Prostokąt opisujący rozmiary ekranu
        /// </summary>
        public Rectangle ViewRect
        {
            get { return viewRect; }
        }

        #endregion

        #region konstruktor i inicjalizacja

        /// <summary>
        /// Główny konstruktor klasy.
        /// </summary>
        public ScreenManager(RTSGame game) : base(game) {
        }

        /// <summary>
        /// Inicjalizacja komponentu
        /// </summary>
        public override void Initialize()
        {
            base.Initialize();

            isInitialised = true;
        }

        /// <summary>
        /// Wczytanie danych przez komponent
        /// </summary>
        protected override void LoadContent()
        {
            ContentManager content = Game.Content;

            // obiekt exSpriteBatch wspolny dla wszystkich ekranów
            exSpriteBatch = new ExtendedSpriteBatch(GraphicsDevice);

            viewRect = Game.GraphicsDevice.Viewport.Bounds;

            // wspolna czcionka dla wszystkich ekranów
            font = content.Load<SpriteFont>("menufont2");

            foreach (GameScreen screen in screenList)
            {
                screen.LoadContent();
            }
        }

        #endregion

        #region metody draw i update


        /// <summary>
        /// Rysowanie aktywnych ekranów gry
        /// </summary>
        /// <param name="gameTime">Parametr przekazywany przez framework</param>
        public override void Draw(GameTime gameTime)
        {
            exSpriteBatch.Begin(SpriteSortMode.BackToFront, 
                                BlendState.AlphaBlend,
                                null,
                                null,
                                null,
                                null,
                                ((RTSGame) Game).Camera.GetTransformation(GraphicsDevice));

            // rysujemy tylko stany aktywne
            foreach (GameScreen state in screenList)
            {
                if (!state.IsActive)
                    continue;

                state.Draw(gameTime);
                
            }

            exSpriteBatch.End();
        }

        /// <summary>
        /// Metoda zarządzająca update'owaniem ekranów w liście.
        /// Aktualizuje ona stan urządzeń wejściowych oraz przegląda
        /// listę ekranów od tyłu. Pierwszemu aktywnemu napotkanemu ekranowi
        /// przekazuje InputState.
        /// </summary>
        /// <param name="gameTime"></param>
        public override void Update(GameTime gameTime)
        {
            // wczytaj nowy rozmiar okna
            viewRect = Game.GraphicsDevice.Viewport.Bounds;

            // wczytaj nowe stany kontrolerów
            input.Update();

            // zmienna wykrywająca pierwszy aktywny ekran
            bool otherScreenHasFocus = !Game.IsActive;
            bool eventHandled = false;

            // kopiowanie listy ekranów do listy zapasowej
            screensToUpdate.Clear();

            foreach (GameScreen screen in screenList)
                screensToUpdate.Add(screen);

            bool popupOn = false;

            // przegladanie listy od tyłu
            while (screensToUpdate.Count > 0)
            {
                GameScreen screen = screensToUpdate[screensToUpdate.Count - 1];

                screensToUpdate.RemoveAt(screensToUpdate.Count - 1);

                if (screen.IsActive)
                {
                    if (!popupOn && !otherScreenHasFocus)
                    {
                        screen.Update(gameTime);
                        screen.HandleInput(input, otherScreenHasFocus, ref eventHandled);
                    }

                    if (screen.IsPopup)
                        popupOn = true;
                } 
            }
        }


        #endregion

        #region metody publiczne


        /// <summary>
        /// Dodawanie nowego stanu gry
        /// </summary>
        /// <param name="screen">
        /// Instancja ekranu gry do dodania. Musi dziedziczyć z GameScreen.
        /// </param>
        public void AddScreen(GameScreen screen)
        {
            screen.ScreenManager = this;

            screenList.Add(screen);

            if(isInitialised)
                screen.LoadContent();

        }

        /// <summary>
        /// Usuwanie stanu gry
        /// </summary>
        /// <param name="screen">Stan gry do usunięcia</param>
        public void RemoveScreen(GameScreen screen)
        {
            screen.UnloadContent();
            screenList.Remove(screen);
        }

        /// <summary>
        /// Funkcja zwracająca tablicę wszystkich ekranów
        /// </summary>
        /// <returns>Tablica wszystkich ekranów</returns>
        public GameScreen[] GetScreens()
        {
            return screenList.ToArray();
        }

        #endregion
    }
}
